// Copyright (c) 2022 Presto Labs Pte. Ltd.
// Author: junglekim
#pragma once

#include <algorithm>
#include <memory>
#include <limits>
#include <iomanip>
#include <math.h>

#include <gflags/gflags.h>

#include "coin2/exchange/base/symbology/product_info.h"
#include "coin2/exchange/factory/symbology/product.h"
#include "coin2/exchange/base/symbology/product_encyclopedia.h"
#include "coin2/exchange/base/feed/timer/deadline_timer.h"
#include "coin2/strategy/order_executor.pb.h"
#include "coin2/strategy/order_executor/mm_executor.h"
#include "coin2/strategy/order_executor/executor.h"
#include "coin2/strategy/order_executor/layering_executor3.h"
#include "coin2/strategy/strategy.h"
#include "coin2/strategy/vmm/vmm_util.h"

using coin2::app::Driver;
using coin2::exchange::base::feed::FeedUpdate;
using coin2::exchange::base::feed::FeedTimer;
using coin2::exchange::base::feed::timer::DeadlineTimer;
using coin2::exchange::base::order::IOrderSubsystem;
using coin2::exchange::base::order::OrderUpdate;
using coin2::strategy::PassiveExecutorConfig;
using IProductHolder = coin2::exchange::base::symbology::IProductHolder;
using coin2::exchange::base::symbology::IProduct;
using coin2::exchange::base::symbology::IProductInfo;
using coin2::exchange::base::symbology::ProductEncyclopedia;
using coin2::strategy::order_executor::PassiveOrderExecutor;
using coin2::strategy::order_executor::LayeringExecutor3;
using coin2::strategy::order_executor::LayeringLevels;
using coin::proto::OrderSide;
using coin::proto::OrderEvent;
using coin2::strategy::order_executor::LayeringLevels;
using coin2::strategy::vmm::logspace;

namespace coin2::appcoin2::support::mm_helper {

class MMDefenderStrategy : public coin2::strategy::IStrategy {
 public:
  MMDefenderStrategy(
      double lot_size_quote,
      double order_update_period,
      std::string start_datetime,
      std::string end_datetime,
      double buy_min_price,
      double buy_max_price,
      double sell_min_price,
      double sell_max_price,
      double buy_layer_num,
      double sell_layer_num)
      : lot_size_quote_(lot_size_quote),
        order_update_period_(order_update_period * 1e9),
        start_datetime_(start_datetime),
        end_datetime_(end_datetime),
        buy_min_price_(buy_min_price),
        buy_max_price_(buy_max_price),
        sell_min_price_(sell_min_price),
        sell_max_price_(sell_max_price),
        buy_layer_num_(buy_layer_num),
        sell_layer_num_(sell_layer_num) {
    CHECK(lot_size_quote_ > 0) << "lot_size_quote must be greater than zero";
  }

  void Init(Driver* driver) override;
  void onCleanup(Driver* driver) override;
  void onCleanupAux(Driver*, int);

  // void onBookFeed(const FeedUpdate& upd) override;
  // void onTradeFeed(const FeedUpdate& upd) override;
  void onHeartbeatFeed(const FeedTimer& timer) override;
  void onAccountOrder(const OrderUpdate& upd) override;
  void onAccountInfo(const OrderUpdate& upd) override;
  // void onOrderLog(const OrderGatewayLog& log) override;

  void Report();
  void CheckDone (int64_t timestamp);
  int64_t Readable2Epoch(std::string readable);
  std::string GetSymbol(const IProduct& product);
  double RoundUp(double value, int decimal);

 private:
  IOrderSubsystem* os();

  Driver* driver_{nullptr};
  std::unique_ptr<LayeringExecutor3> executor_;
  const IProductHolder* product_holder_;
  PassiveExecutorConfig exe_config_;
  double lot_size_quote_;
  int64_t order_update_period_;

  std::string start_datetime_;
  std::string end_datetime_;
  int64_t timestamp0_{0};
  int64_t timestamp1_{std::numeric_limits<int64_t>::max()};

  double buy_min_price_{0};
  double buy_max_price_{0};
  double sell_min_price_{0};
  double sell_max_price_{0};
  double buy_layer_num_{0};
  double sell_layer_num_{0};
  double buy_lot_size_{0};
  double sell_lot_size_{0};

  std::vector<LayeringLevels::Level> buy_level_;
  std::vector<LayeringLevels::Level> sell_level_;
  
  int64_t last_report_ts_{0};

  std::pair<bool, bool> done_{true, true};
  std::unordered_map<double, double> buy_fills_;
  std::unordered_map<double, double> sell_fills_;
  double initial_pos_{0};
  std::unique_ptr<DeadlineTimer> timer_;
  std::string symbol_;
  std::string owner_;
};
}  // namespace coin2::appcoin2::support::mm_helper